package com.amumu.drama.common.commpont.notify;

import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.enums.EnvUtils;
import com.ctrip.framework.foundation.Foundation;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.springframework.util.DigestUtils;

import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author sunzhuang
 * @date 2024/10/25 10:08
 * @slogan 天下风云出我辈, 一入代码岁月催
 * @desc todo
 */

public class NotifyUtils {

    private static final Cache<String, AtomicInteger> CONTENT_CACHE = Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();
    private static final String PATTERN = "%s\n\n%s";
    private static final String TITLE;

    static {
        String machine = System.getProperty("app.id") + "@" + getHostName();
        Env env = getEnv();
        if (env == Env.DEV && org.springframework.util.StringUtils.endsWithIgnoreCase(machine, ".local")) {
            TITLE = String.format("[LOCAL] %s", machine);
        } else {
            TITLE = String.format("[%s] %s", env, machine);
        }
    }

    /**
     * 推送INFO消息
     * 若logger参数不为null,则自动写入logger.info(msg)
     */
    public static void pushInfo(String msg, Logger logger) {
        if (StringUtils.isBlank(msg)) {
            return;
        }

        if (logger != null) {
            logger.info(msg);
        }

        if (NotifyConfigLoader.NOTIFY_CONFIG.getEnable() == 0) {
            return;
        }

        String content = "\n信息：" + msg;
        pushToQueue(content);
    }

    /**
     * 推送报警消息
     * 此方法本应由Log.error自动触发，不推荐手动调用
     */
    public static void pushError(String msg, Throwable t) {
        if (StringUtils.isBlank(msg) && t == null) {
            return;
        }

        if (NotifyConfigLoader.NOTIFY_CONFIG.getEnable() == 0) {
            return;
        }

        String[] ignoreIfMatchStart = NotifyConfigLoader.NOTIFY_CONFIG.getErrorIgnoreIfMatchStart();
        if (ignoreIfMatchStart != null && ignoreIfMatchStart.length > 0) {
            if (StringUtils.startsWithAny(msg, ignoreIfMatchStart)) {
                return;
            }
        }

        String[] ignoreIfMatchEnd = NotifyConfigLoader.NOTIFY_CONFIG.getErrorIgnoreIfMatchEnd();
        if (ignoreIfMatchEnd != null && ignoreIfMatchEnd.length > 0) {
            if (StringUtils.endsWithAny(msg, ignoreIfMatchEnd)) {
                return;
            }
        }

        String content = buildErrorContent(msg, t);
        pushToQueue(content);
    }

    private static String buildErrorContent(String msg, Throwable t) {
        String content = "\n异常：" + msg;

        int printStackTrace = NotifyConfigLoader.NOTIFY_CONFIG.getPrintStackTrace();
        if (t == null || printStackTrace == 0) {
            return content;
        }

        int printStackTraceDepth = NotifyConfigLoader.NOTIFY_CONFIG.getPrintStackTraceDepth();
        if (printStackTraceDepth <= 0) {
            return content + "\n堆栈：" + Throwables.getStackTraceAsString(t);
        }
        return content + "\n堆栈：" + getStackTrace(t, printStackTraceDepth);
    }

    private static void pushNotifyToQueue(String content) {
        try {
            Notifier.Holder.INSTANT.addMessage(content);
        } catch (Throwable e) {
            Notifier.Holder.INSTANT.addMessage("NotifyUtils.notify error");
        }
    }

    private static void pushToQueue(String content) {
        try {
            String md5 = DigestUtils.md5DigestAsHex(content.getBytes(StandardCharsets.UTF_8));
            AtomicInteger count = CONTENT_CACHE.get(md5, k -> new AtomicInteger(0));
            final int index = count.incrementAndGet();
            if (index != 1 && index % 10 != 0) {
                return;
            }

            final String traceId = ThreadContext.get("traceId");
            final String userId = ThreadContext.get("userId");
            String prefixTrace = StringUtils.EMPTY;
            if (!StringUtils.isBlank(traceId)) {
                prefixTrace = "\n链路：traceId=" + traceId;
                if (!StringUtils.isBlank(userId)) {
                    prefixTrace += "   userId=" + userId;
                }
            }
            content = "次数：" + index + "（5分钟滑动窗口）" + prefixTrace + content;
            content = String.format(PATTERN, TITLE, content);
            int maxMsgLength = NotifyConfigLoader.NOTIFY_CONFIG.getMaxMsgLength();
            if (content.length() > maxMsgLength - 100) {
                content = content.substring(0, maxMsgLength - 100);
            }

            Notifier.Holder.INSTANT.addMessage(content);
        } catch (Throwable e) {
            Notifier.Holder.INSTANT.addMessage("NotifyUtils.notify error");
        }
    }

    private static String getHostName() {
        String hostName = "UNKNOWN";
        String pidAtHostName = ManagementFactory.getRuntimeMXBean().getName();
        int idx = pidAtHostName.indexOf('@');
        if (idx > 0) {
            hostName = pidAtHostName.substring(idx + 1);
        }
        return hostName;
    }

    private static Env getEnv() {
        return EnvUtils.transformEnv(Foundation.server().getEnvType());
    }

    private static String getStackTrace(Throwable t, int maxLine) {
        List<Throwable> throwableList = Throwables.getCausalChain(t);
        if (throwableList == null || throwableList.size() == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < throwableList.size(); i++) {
            Throwable throwable = throwableList.get(i);
            StackTraceElement[] stackTrace = throwable.getStackTrace();
            int lineSize = Math.min(stackTrace.length, maxLine);
            if (i > 0) {
                sb.append("\n\nCaused by: ");
            }
            sb.append(throwable.getClass().getName());
            sb.append(": ");
            sb.append(throwable.getMessage());
            for (int l = 0; l < lineSize; l++) {
                sb.append("\nat ");
                sb.append(stackTrace[l]);
            }
            sb.append(" ...");
        }

        return sb.toString();
    }


    // 转义消息内容中的特殊字符
    public static String escapeMessage(String msg) {
        // 这里可以根据需要对msg中的特殊字符进行转义
        // 例如，转义双引号：
        return msg.replace("\"", "\\\"");
    }
}